package com.hyt.it.ogt.log.interceptor.interceptor.aspect;

import com.hyt.core.util.UUIDUtils;
import com.hyt.it.ogt.log.interceptor.constants.LogCommonConstants;
import com.hyt.it.ogt.log.interceptor.interceptor.TraceContext;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * @author 朱振宇
 * @since 2022-12-05
 */
@Slf4j
@Aspect
@Component
@ConditionalOnProperty(value = "traceInterceptor.controller.enabled", havingValue = "true")
public class ControllerLogAspect {

    private static final String TRACE_ID = "TRACE_ID";

    @Around("execution(public * com.hyt..*.controller..*.*(..)) || execution(public * com.hyt..*.api..*.*(..))")
    public Object execute(final ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            recordBefore(joinPoint);
            Object result = joinPoint.proceed(joinPoint.getArgs());
            recordAfter(result);
            return result;
        } finally {
            TraceContext.removeAll();
            MDC.clear();
        }
    }

    public void recordBefore(final ProceedingJoinPoint joinPoint) {
        try {
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (Objects.isNull(requestAttributes)) {
                return;
            }
            HttpServletRequest request = requestAttributes.getRequest();
            String traceId = request.getHeader(LogCommonConstants.TRACE_ID_HEADER);

            if (StringUtils.isEmpty(traceId)) {
                traceId = UUIDUtils.randomUUID();
            }
            Object[] args = joinPoint.getArgs();
            String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < argNames.length; i++) {
                sb.append(argNames[i]).append("=").append(args[i]).append(",");
            }
            String requestBody = "";
            if (sb.length() > 1000) {
                requestBody = sb.deleteCharAt(sb.length() - 1).substring(0, 1000) + "...";
            } else if(sb.length() > 0) {
                requestBody = sb.deleteCharAt(sb.length() - 1).toString();
            }
            TraceContext.putTraceId(traceId);
            TraceContext.putRequestUri(request.getRequestURI());
            TraceContext.putMethodName(joinPoint.getSignature().getName());
            TraceContext.putRequestBody(requestBody);
            MDC.put(TRACE_ID, traceId);
            TraceContext.start();
            log.info("url请求:{{}},traceId:{{}},method:{{}},param:{{}}", request.getRequestURI(), traceId, joinPoint.getSignature().getName(), requestBody);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
    }

    public void recordAfter(Object result) {
        try {
            long costTime = TraceContext.stopAndGet();
            String responseBody = "";
            if(Objects.nonNull(result)) {
                if(result.toString().length() > 1000) {
                    responseBody = result.toString().substring(0, 1000) + "...";
                } else {
                    responseBody = result.toString();
                }
            }
            log.info("url响应:{{}},traceId:{{}},methodName:{{}},param:{{}},resp:{{}},耗时:{{}}", TraceContext.getRequestUri(), TraceContext.getTraceId(), TraceContext.getMethodName(), TraceContext.getRequestBody(), responseBody, costTime + "ms");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
